﻿using System;
using System.IO;

using Nop.Core;
using Nop.Core.Domain.Media;
using Nop.Core.Infrastructure;
using Nop.Services.Configuration;
using Nop.Services.Logging;

using Amazon;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Util;

namespace RonsHouse.Plugin.Misc.AlternativeStorage.Services
{
	public partial class AmazonS3StorageProvider : IAlternativeStorageProvider
	{
		private readonly ISettingService _settingService;
		private readonly IWebHelper _webHelper;
		private readonly ILogger _logger;

		private readonly string FILE_NAME_FORMAT = "{0}_{1}{2}";

		public AmazonS3StorageProvider()
		{
			this._settingService = EngineContext.Current.Resolve<ISettingService>();
			this._webHelper = EngineContext.Current.Resolve<IWebHelper>();
			this._logger = EngineContext.Current.Resolve<ILogger>();
		}

		public AmazonS3StorageProvider(ISettingService settingService, IWebHelper webHelper, ILogger logger)
		{
			this._settingService = settingService;
			this._webHelper = webHelper;
			this._logger = logger;
		}

		public byte[] Get(Download download)
		{
			byte[] bytes = null;

			var settings = _settingService.LoadSetting<AlternativeStorageSettings>();
			if (HasValidSettings(settings))
			{
				using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(settings.AmazonS3AccessKey, settings.AmazonS3SecretKey))
				{
					var request = new GetObjectRequest()
					{
						BucketName = settings.AmazonS3PictureBucket,
						Key = String.Format(FILE_NAME_FORMAT, download.Id.ToString(), download.Filename, download.Extension)
					};

					using (MemoryStream stream = new MemoryStream())
					{
						var response = client.GetObject(request);
						try
						{
							BufferedStream buffStream = new BufferedStream(response.ResponseStream);
							byte[] buffer = new byte[0x2000];
							int count = 0;
							while ((count = buffStream.Read(buffer, 0, buffer.Length)) > 0)
							{ 
								stream.Write(buffer, 0, count);
							}
						}
						catch (Exception ex)
						{
							_logger.Error(String.Format("Unable to get file '{0}' from Amazon S3", request.Key), ex);
						}
						finally { }

						bytes = stream.ToArray();
					}
				}
			}

			return bytes;
		}

		public void Save(Download download, byte[] data)
		{
			var settings = _settingService.LoadSetting<AlternativeStorageSettings>();
			if (HasValidSettings(settings))
			{
				using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(settings.AmazonS3AccessKey, settings.AmazonS3SecretKey))
				{ 
					if (!BucketExists(client, settings.AmazonS3PictureBucket))
					{
						var putBucketRequest = new PutBucketRequest
						{
							BucketName = settings.AmazonS3PictureBucket
						};
						client.PutBucket(putBucketRequest);
					}

					using (MemoryStream stream = new MemoryStream(data))
					{ 
						var putFileRequest = new PutObjectRequest()
						{
							BucketName = settings.AmazonS3PictureBucket,
							Key = String.Format(FILE_NAME_FORMAT, download.Id.ToString(), download.Filename, download.Extension),
							InputStream = stream
						};

						stream.Close();
					}
				}
			}
		}

		public void Delete(Download download)
		{
			var settings = _settingService.LoadSetting<AlternativeStorageSettings>();
			if (HasValidSettings(settings))
			{
				using (var client = Amazon.AWSClientFactory.CreateAmazonS3Client(settings.AmazonS3AccessKey, settings.AmazonS3SecretKey))
				{
					var request = new DeleteObjectRequest()
					{
						BucketName = settings.AmazonS3PictureBucket,
						Key = String.Format(FILE_NAME_FORMAT, download.Id.ToString(), download.Filename, download.Extension)
					};

					var response = client.DeleteObject(request);
				}

				var filePath = _webHelper.MapPath(Path.Combine(settings.FileSystemPicturePath, String.Format(FILE_NAME_FORMAT, download.Id.ToString(), download.Filename, download.Extension)));
				File.Delete(filePath);
			}
		}

		public bool HasValidSettings(AlternativeStorageSettings settings)
		{
			return !String.IsNullOrEmpty(settings.AmazonS3AccessKey) && 
				!String.IsNullOrEmpty(settings.AmazonS3SecretKey) && 
				!String.IsNullOrEmpty(settings.AmazonS3PictureBucket);
		}

		private bool BucketExists(IAmazonS3 client, string bucket)
		{
			bool exists = false;
			try
			{
				ListBucketsResponse response = client.ListBuckets();
				foreach (S3Bucket b in response.Buckets)
				{
					if (b.BucketName == bucket)
					{
						exists = true;
						break;
					}
						
				}
			}
			catch (Exception ex)
			{
				_logger.Error("Unable to retrieve bucket list from Amazon S3", ex);
			}

			return exists;
		}
	}
}